﻿using System.Reflection;
using System.Security.Claims;
using ApiLib.AppStart;
using ApiLib.Common.Attributes;
using ApiLib.Common.Attributes.IocAttributes;
using ApiLib.Controllers;
using ApiLib.Helpers;
using ApiLib.Models;
using ApiLib.Utils;
using Furion;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;

namespace ApiLib.Common.Filters;

/// <summary>
/// 方法拦截
/// </summary>
public class DefaultActionFilter : IAsyncActionFilter
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <param name="next"></param>
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        WebUtil.LogReq("请求开始");
        var requestPath = context.HttpContext.Request.Path.Value;
        if (requestPath == null)
        {
            WebUtil.LogReq("非法路径");
            throw new MyUnauthorizedAccessException("非法路径");
        }

        //============== 这里是执行方法之前获取数据 ====================
        // 获取控制器、路由信息
        if (context.ActionDescriptor is ControllerActionDescriptor actionDescriptor)
        {
            if (GlobalConfig.BaseAuthorization != null)
            {
                if (!CheckPermission(actionDescriptor, context.HttpContext.Request))
                {
                    WebUtil.LogReq("没有权限");
                    // context.Result = new ForbidResult();
                    // throw new MyActionException("没有权限");
                    throw new MyUnauthorizedAccessException("没有权限");
                }
            }

            // 获取 HttpContext 和 HttpRequest 对象
            var httpContext = context.HttpContext;
            var httpRequest = httpContext.Request;
            // var body = await httpRequest.Body.ReadAsString();
            var startTime = CommonUtil.LocalDateTime();

            if (Cacheable(context,actionDescriptor, httpRequest))
            {
                return;
            }
            //============== 这里是执行方法之后获取数据 ====================
            var actionContext = await next();

            var result=actionContext.Result is JsonResult jsonResult ? jsonResult.Value:null;
            CacheSave(actionDescriptor, result);

            var endTime = CommonUtil.LocalDateTime();
            var timeSpan = endTime - startTime;
            var httpLog = GlobalConfig.AppConfig != null && GlobalConfig.AppConfig.HttpLog;
            var httpLogBody = GlobalConfig.AppConfig != null && GlobalConfig.AppConfig.HttpLogBody;
            var httpLogResult = GlobalConfig.AppConfig != null && GlobalConfig.AppConfig.HttpLogResult;
            if (httpLog)
            {
                var realIp = context.HttpContext.Request.Headers.TryGetValue("X-Real-IP", out var value)
                    ? value.ToString()
                    : context.HttpContext.GetRemoteIpAddressToIPv4();
                var requestLog = new SysRequestLog
                {
                    Descriptor = actionDescriptor.ControllerTypeInfo.Name,
                    // 获取请求的 Url 地址
                    Path = context.HttpContext.Request.Path + context.HttpContext.Request.QueryString,
                    Body = httpLogBody ? context.ActionArguments.Serialize() : "",
                    Result = httpLogResult ? result.Serialize() : "",
                    // 获取客户端 Ipv4 地址
                    Ip = realIp,
                    // 获取操作人（必须授权访问才有值）"userId" 为你存储的 claims type，jwt 授权对应的是 payload 中存储的键名
                    // User = App.User?.FindFirstValue(ClaimTypes.Sid) ?? "",
                    User = context.Controller is BaseController controller ? controller.Sid : "",
                    Succeed = actionContext.Exception == null,
                    StartTime = startTime,
                    EndTime = endTime,
                    TimeSpan = timeSpan.TotalMilliseconds
                };

                if (requestLog.Body.Length > 600)
                {
                    requestLog.Body = requestLog.Body[..500];
                }

                if (requestLog.Result.Length > 20000)
                {
                    requestLog.Result = requestLog.Result[..18000];
                }

                try
                {
                    await DbHelper.Db.Insertable(requestLog).ExecuteCommandAsync();
                }
                catch (Exception e)
                {
                    WebUtil.LogExp("插入Http日志异常",e);
                }

                if (MongoUtil.UseMongo)
                {
                    MongoUtil.Insert(requestLog);
                    if (actionContext.Exception != null)
                    {
                        WebUtil.LogReq($"请求异常：{actionContext.Exception.Message}");
                    }

                    WebUtil.LogReq($"请求耗时：{timeSpan.TotalMilliseconds}");
                }
            }

            return;
        }

        // await Task.CompletedTask;//跳过处理
        await next(); //下一步，继续处理
    }

    /// <summary>
    /// 检查权限
    /// </summary>
    /// <param name="descriptor"></param>
    /// <param name="request"></param>
    /// <returns></returns>
    private bool CheckPermission(ControllerActionDescriptor descriptor, HttpRequest request)
    {
        // var isDefined = descriptor.MethodInfo.IsDefined(typeof(PermissionAttribute),true);
        // var anonymous = descriptor.MethodInfo.GetCustomAttribute<AllowAnonymousAttribute>();
        var anonymous = descriptor.ControllerTypeInfo.IsDefined(typeof(AllowAnonymousAttribute), true) ||
                        descriptor.MethodInfo.IsDefined(typeof(AllowAnonymousAttribute), true);
        if (anonymous)
        {
            return true;
        }
        
        var baseUser = GlobalConfig.BaseAuthorization?.Authorization(request);
        if (baseUser == null)
        {
            return false;
        }

        var permission = descriptor.MethodInfo.GetCustomAttribute<PermissionAttribute>();
        return CheckUserPermission(baseUser, descriptor, permission);
    }

    /// <summary>
    /// 检查用户权限
    /// </summary>
    /// <param name="baseUser"></param>
    /// <param name="descriptor"></param>
    /// <param name="attribute"></param>
    /// <returns></returns>
    private bool CheckUserPermission(IBaseUser baseUser, ControllerActionDescriptor descriptor,
        PermissionAttribute? attribute = null)
    {
        if (attribute == null)
        {
            return true;
        }

        if (baseUser.IsSuperAdmin())
        {
            return true;
        }

        //验证角色：超级管理员或者拥有对于角色的可以访问
        var userRoles = baseUser.GetRoles();
        if (!string.IsNullOrEmpty(attribute.Role) && userRoles != null && userRoles.Contains(attribute.Role))
        {
            return true;
        }

        //验证权限
        var userPerms = baseUser.GetPermissions();
        var actionPerm = !string.IsNullOrEmpty(attribute.Perm)
            ? attribute.Perm
            : descriptor.ControllerName + ":" + descriptor.ActionName;

        return userPerms?.Contains(actionPerm) ?? baseUser.HasPermission(descriptor, attribute);
    }

    /// <summary>
    /// 检查缓存
    /// </summary>
    /// <param name="context"></param>
    /// <param name="descriptor"></param>
    /// <param name="request"></param>
    /// <returns></returns>
    private bool Cacheable(ActionExecutingContext context,ControllerActionDescriptor descriptor, HttpRequest request)
    {
        var cacheable = descriptor.MethodInfo.GetCustomAttribute<CacheableAttribute>();
        if (cacheable==null)
        {
            return false;
        }
        
        var cacheName=cacheable.GetCacheName(descriptor);
        var cacheGet = DbHelper.CacheGet<object>(cacheName);
        if (cacheGet != null )
        {
            context.Result = new JsonResult(cacheGet) { StatusCode = 200 };
            return true;
        }

        return false;
    }
    /// <summary>
    /// 存储缓存
    /// </summary>
    /// <param name="descriptor"></param>
    /// <param name="data"></param>
    private void CacheSave(ControllerActionDescriptor descriptor, object? data)
    {
        if (data == null)
        {
            return;
        }
        var cacheable = descriptor.MethodInfo.GetCustomAttribute<CacheableAttribute>();
        if (cacheable==null)
        {
            return;
        }
        
        var cacheName=cacheable.GetCacheName(descriptor);
        DbHelper.CacheSet(cacheName,data,cacheable.Expiration);
    }
}